package org.interaction3d.assembly.convert;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;

/**
 *
 * @author micha
 */
public class Mesh
{
    public static class PrimitiveGroup
    {
        public final String material;
        public final String type;
        public final int[][] primitives;

        public PrimitiveGroup(String name, String type, int[][] primitives)
        {
            this.material = name;
            this.type = type;
            this.primitives = primitives;
        }       
    }

    public static class VertexElement
    {
        public final String name;
        public final double[][] coordinates;
        public final int[] duplicates;
        public final int map;

        public VertexElement(String name, double[][] coordinates)
        {
            this(name, coordinates, new int[0], -1);
        }

        public VertexElement(String name, double[][] coordinates, int[] duplicates)
        {
            this(name, coordinates, duplicates, -1);
        }

        public VertexElement(String name, double[][] coordinates, int map)
        {
            this(name, coordinates, new int[0], map);
        }

        public VertexElement(String name, double[][] coordinates, int[] duplicates, int map)
        {
            this.name = name;
            this.coordinates = coordinates;
            this.duplicates = duplicates;
            this.map = map;
        }
    }

    int numVertices;
    int[][] maps;
    VertexElement[] elements;
    PrimitiveGroup[] triangleGroups;

    public Mesh(VertexElement[] elements, int[][] maps, PrimitiveGroup[] primitiveGroup)
    {
        if(elements.length < 1) throw new IllegalArgumentException("There must be at least a singel VertexElement");
        
        numVertices = elements[0].map >= 0
                    ? maps[elements[0].map].length
                    : elements[0].coordinates.length + elements[0].duplicates.length;

        
        for(int i=1; i<elements.length; i++)
        {
            int count = elements[i].map >= 0
                    ? maps[elements[i].map].length
                    : elements[i].coordinates.length + elements[i].duplicates.length;
            
            if(numVertices != count)
            {
                throw new IllegalArgumentException("All VertexElements must agree on the vertex count");
            }
        }
        
        this.elements = elements;
        this.maps = maps;
        this.triangleGroups = primitiveGroup;
    }

    public void writeTo(OutputStream out) throws IOException
    {
        //Charset charset = Charset.defaultCharset();
        Charset charset = Charset.forName("UTF-8");

        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, charset));
        //BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
        try
        {
            writer.write("<?xml version=\"1.0\" encoding=\"" + charset.toString() + "\" ?>");
            writer.newLine();
            writeTo(writer);
        }
        finally
        {
            writer.close();
        }
    }

    private void writeTo(BufferedWriter writer) throws IOException
    {
        String xmls = "http://xml.interaction3d.org/assembly/Mesh";
        String xsi  = "http://www.w3.org/2001/XMLSchema-instance";
        String loc  = "http://xml.interaction3d.org/assembly Mesh.xsd";
        writer.write("<Mesh xmlns=\"" + xmls + "\" xmlns:xsi=\"" + xsi + "\" xsi:schemaLocation=\"" + loc + "\">");
        writer.newLine();

        writer.write("\t<Vertices count=\"" + numVertices + "\">");
        writer.newLine();

        {
            writer.write("\t\t<Elements count=\"" + elements.length + "\">");
            writer.newLine();

            for(int i=0; i<elements.length; i++)
            {
                VertexElement element = elements[i];

                if(element.map >= 0)
                {
                    writer.write("\t\t\t<Element name=\"" + element.name +"\" map=\"" + element.map +"\">");
                }
                else
                {
                    writer.write("\t\t\t<Element name=\"" + element.name +"\">");
                }
                writer.newLine();

                {
                    double[][] coordinates = element.coordinates;
                    int[] duplicates      = element.duplicates;
                    
                    int count = coordinates.length+duplicates.length;
                    int split = coordinates.length;
                    int dim = coordinates[0].length;

                    if(split == count)
                    {
                        writer.write("\t\t\t\t<Coordinates dimension=\"" + dim  +"\" count=\"" + count +"\">");
                    }
                    else
                    {
                        writer.write("\t\t\t\t<Coordinates dimension=\"" + dim  +"\" count=\"" + count  +"\" split=\"" + split +"\">");
                    }

                    for(int j=0; j<coordinates.length; j++)
                    {
                        if(j > 0 ) writer.write(" ");

                        double[] coordinate = coordinates[j];

                        for(int k=0; k<coordinate.length; k++)
                        {
                            if(k > 0 ) writer.write(" ");

                            writer.write(String.format("%f", coordinate[k]));
                        }
                    }

                    if(duplicates.length > 0)
                    {
                        writer.write("<Split/>");
                        for(int j=0; j<duplicates.length; j++)
                        {
                            if(j > 0 ) writer.write(" ");

                            writer.write(String.format("%d", duplicates[j]));
                        }                        
                    }

                    writer.write("</Coordinates>");
                    writer.newLine();
                }

                writer.write("\t\t\t</Element>");
                writer.newLine();
            }

            writer.write("\t\t</Elements>");
            writer.newLine();
        }

        if(maps.length > 0) // maps
        {
            writer.write("\t\t<Mapping count=\"" + maps.length + "\">");

            boolean init = true;
            for(int i=0; i<numVertices; i++) for(int j=0; j<maps.length; j++)
            {                
                if(init)
                {                    
                    init = false;
                }
                else
                {
                     writer.write(" ");
                }

                writer.write(String.format("%d", maps[j][i]));
            }

            writer.write("</Mapping>");
            writer.newLine();
        }

        writer.write("\t</Vertices>");
        writer.newLine();

        int numPrimitiveGroups = triangleGroups.length;
        int numTrianges = 0;
        for(int i=0; i<triangleGroups.length; i++)
        {
            if(triangleGroups[i].type.equalsIgnoreCase("Triangles"))
            {
                numTrianges += triangleGroups[i].primitives.length;
            }
        }

        String primtives = "";
        if(numTrianges > 0)
        {
            primtives += " triangles=\"" + numTrianges + "\"";
        }
        
        writer.write("\t<Primitives groups=\"" + numPrimitiveGroups + "\"" +  primtives + ">");
        writer.newLine();

        for(int i=0; i<triangleGroups.length; i++)
        {
            String name = triangleGroups[i].material;
            String type = triangleGroups[i].type;
            int[][] primitives = triangleGroups[i].primitives;

            writer.write("\t\t<" + type + " material=\"" + name + "\" count=\"" + primitives.length + "\">");

            for(int j=0; j<primitives.length; j++)
            {
                if(j > 0) writer.write(" ");

                int[] indices = primitives[j];

                for(int k=0; k<indices.length; k++)
                {
                    if(k > 0 ) writer.write(" ");

                    writer.write(String.format("%d", indices[k]));
                }                
            }

            writer.write("</" + type + ">");
            writer.newLine();
        }

        writer.write("\t</Primitives>");
        writer.newLine();

        writer.write("</Mesh>");
    }

}
